<!DOCTYPE html>
<html lang="en-US">
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width,initial-scale=1" />
    <title>实现一个vdom | aiyoudiao</title>
    <meta name="generator" content="VuePress 1.9.10" />
    <link rel="icon" href="/img/blog.ico">
    <script src="https://cdn.jsdelivr.net/npm/live2d-widget@3.1.4/lib/L2Dwidget.min.js"></script> <meta name="description" content="码二~">
    <meta name="keywords" content="前端博客,个人技术博客,前端,前端开发,前端框架,web前端,前端面试题,技术文档,学习,面试,JavaScript,js,ES6,TypeScript,vue,python,css3,html5,Node,git,github,gitee,markdown">
    <meta name="theme-color" content="#11a8cd">
    <meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no"> <link rel="preload" href="/assets/css/0.styles.146197cf.css" as="style"><link rel="preload" href="/assets/js/app.bd2fbc77.js" as="script"><link rel="preload" href="/assets/js/3.72c9c947.js" as="script"><link rel="preload" href="/assets/js/102.c3e2dcae.js" as="script"><link rel="preload" href="/assets/js/42.4251ca36.js" as="script"><link rel="prefetch" href="/assets/js/1.4ed4671d.js"><link rel="prefetch" href="/assets/js/10.bd6ddb58.js"><link rel="prefetch" href="/assets/js/100.20d2348f.js"><link rel="prefetch" href="/assets/js/101.ba7b784c.js"><link rel="prefetch" href="/assets/js/103.0f4c50f3.js"><link rel="prefetch" href="/assets/js/104.ef47a111.js"><link rel="prefetch" href="/assets/js/105.2e00f516.js"><link rel="prefetch" href="/assets/js/106.b50e19b9.js"><link rel="prefetch" href="/assets/js/107.e125a8f6.js"><link rel="prefetch" href="/assets/js/108.770493ab.js"><link rel="prefetch" href="/assets/js/109.74766d7b.js"><link rel="prefetch" href="/assets/js/11.f786a5ee.js"><link rel="prefetch" href="/assets/js/110.0b0ee5b4.js"><link rel="prefetch" href="/assets/js/111.835b0e44.js"><link rel="prefetch" href="/assets/js/112.352fa217.js"><link rel="prefetch" href="/assets/js/113.4e908557.js"><link rel="prefetch" href="/assets/js/114.7b77996d.js"><link rel="prefetch" href="/assets/js/115.bdc61268.js"><link rel="prefetch" href="/assets/js/116.d5da9b8b.js"><link rel="prefetch" href="/assets/js/117.35ab1f9f.js"><link rel="prefetch" href="/assets/js/118.517c151d.js"><link rel="prefetch" href="/assets/js/119.f7f49ba8.js"><link rel="prefetch" href="/assets/js/12.3c729a65.js"><link rel="prefetch" href="/assets/js/120.b559598b.js"><link rel="prefetch" href="/assets/js/121.bf8a2f43.js"><link rel="prefetch" href="/assets/js/122.11a0bc97.js"><link rel="prefetch" href="/assets/js/123.2bafdde7.js"><link rel="prefetch" href="/assets/js/124.dc393688.js"><link rel="prefetch" href="/assets/js/125.ed3f389a.js"><link rel="prefetch" href="/assets/js/126.8fd9a57d.js"><link rel="prefetch" href="/assets/js/127.3bf2a1f2.js"><link rel="prefetch" href="/assets/js/128.b9c671d3.js"><link rel="prefetch" href="/assets/js/129.5d331f0d.js"><link rel="prefetch" href="/assets/js/13.7b1a1fe5.js"><link rel="prefetch" href="/assets/js/130.53e4f9c6.js"><link rel="prefetch" href="/assets/js/131.dcc47e1d.js"><link rel="prefetch" href="/assets/js/132.692dcdcd.js"><link rel="prefetch" href="/assets/js/133.e293202c.js"><link rel="prefetch" href="/assets/js/134.593dccf2.js"><link rel="prefetch" href="/assets/js/135.d76d384b.js"><link rel="prefetch" href="/assets/js/136.a519c23c.js"><link rel="prefetch" href="/assets/js/137.b1821288.js"><link rel="prefetch" href="/assets/js/138.5bcea4ef.js"><link rel="prefetch" href="/assets/js/139.076664b0.js"><link rel="prefetch" href="/assets/js/14.35f257b2.js"><link rel="prefetch" href="/assets/js/140.a019e655.js"><link rel="prefetch" href="/assets/js/141.1f70e1c7.js"><link rel="prefetch" href="/assets/js/142.5ed728fd.js"><link rel="prefetch" href="/assets/js/143.1c8cdc78.js"><link rel="prefetch" href="/assets/js/144.b0cb125b.js"><link rel="prefetch" href="/assets/js/145.c0209a76.js"><link rel="prefetch" href="/assets/js/146.551469f4.js"><link rel="prefetch" href="/assets/js/147.1dfd721d.js"><link rel="prefetch" href="/assets/js/148.91d07ef5.js"><link rel="prefetch" href="/assets/js/149.5b88b710.js"><link rel="prefetch" href="/assets/js/15.23bbc29a.js"><link rel="prefetch" href="/assets/js/150.8301107f.js"><link rel="prefetch" href="/assets/js/151.867da089.js"><link rel="prefetch" href="/assets/js/152.935d5046.js"><link rel="prefetch" href="/assets/js/153.f39d8435.js"><link rel="prefetch" href="/assets/js/154.6b9eb2c3.js"><link rel="prefetch" href="/assets/js/155.14283ad4.js"><link rel="prefetch" href="/assets/js/156.2d7c1a2a.js"><link rel="prefetch" href="/assets/js/157.2f28d02f.js"><link rel="prefetch" href="/assets/js/158.151221ae.js"><link rel="prefetch" href="/assets/js/159.ef6d7ffe.js"><link rel="prefetch" href="/assets/js/16.1793aef7.js"><link rel="prefetch" href="/assets/js/160.de54c4ea.js"><link rel="prefetch" href="/assets/js/161.24d4e57c.js"><link rel="prefetch" href="/assets/js/162.632032fe.js"><link rel="prefetch" href="/assets/js/163.fd01cd99.js"><link rel="prefetch" href="/assets/js/164.45f203f5.js"><link rel="prefetch" href="/assets/js/165.aafe4fe1.js"><link rel="prefetch" href="/assets/js/166.1dd1d21c.js"><link rel="prefetch" href="/assets/js/167.5501b3a1.js"><link rel="prefetch" href="/assets/js/168.fbe58b1f.js"><link rel="prefetch" href="/assets/js/169.2cae7f5e.js"><link rel="prefetch" href="/assets/js/17.bbfe63f2.js"><link rel="prefetch" href="/assets/js/170.265f7c9e.js"><link rel="prefetch" href="/assets/js/171.b61f327d.js"><link rel="prefetch" href="/assets/js/172.5d0043fd.js"><link rel="prefetch" href="/assets/js/173.45284bb6.js"><link rel="prefetch" href="/assets/js/174.9130e0c4.js"><link rel="prefetch" href="/assets/js/175.2b38bddd.js"><link rel="prefetch" href="/assets/js/176.9772cf09.js"><link rel="prefetch" href="/assets/js/177.69048ebc.js"><link rel="prefetch" href="/assets/js/178.e10d7ce5.js"><link rel="prefetch" href="/assets/js/179.3789edc0.js"><link rel="prefetch" href="/assets/js/18.0807ded0.js"><link rel="prefetch" href="/assets/js/180.ab675e47.js"><link rel="prefetch" href="/assets/js/181.2e39eff0.js"><link rel="prefetch" href="/assets/js/19.becf5a76.js"><link rel="prefetch" href="/assets/js/2.eb089a4f.js"><link rel="prefetch" href="/assets/js/20.cea59652.js"><link rel="prefetch" href="/assets/js/21.58c43ff1.js"><link rel="prefetch" href="/assets/js/22.f73b825d.js"><link rel="prefetch" href="/assets/js/23.43b13730.js"><link rel="prefetch" href="/assets/js/24.f77f93ca.js"><link rel="prefetch" href="/assets/js/25.7dfaf3fb.js"><link rel="prefetch" href="/assets/js/26.629d28e5.js"><link rel="prefetch" href="/assets/js/27.4fff23ea.js"><link rel="prefetch" href="/assets/js/28.1b8ae389.js"><link rel="prefetch" href="/assets/js/29.d5cce9a0.js"><link rel="prefetch" href="/assets/js/30.961d5519.js"><link rel="prefetch" href="/assets/js/31.121dd1af.js"><link rel="prefetch" href="/assets/js/32.4a3c5df7.js"><link rel="prefetch" href="/assets/js/33.5537f44b.js"><link rel="prefetch" href="/assets/js/34.1d4d4653.js"><link rel="prefetch" href="/assets/js/35.d094209b.js"><link rel="prefetch" href="/assets/js/36.832660c5.js"><link rel="prefetch" href="/assets/js/37.145c3665.js"><link rel="prefetch" href="/assets/js/38.4f369bfe.js"><link rel="prefetch" href="/assets/js/39.ba060044.js"><link rel="prefetch" href="/assets/js/4.66d742f6.js"><link rel="prefetch" href="/assets/js/40.e50e0379.js"><link rel="prefetch" href="/assets/js/41.4ed7617c.js"><link rel="prefetch" href="/assets/js/43.d22b74c4.js"><link rel="prefetch" href="/assets/js/44.59439f9d.js"><link rel="prefetch" href="/assets/js/45.da28bc46.js"><link rel="prefetch" href="/assets/js/46.b8db1176.js"><link rel="prefetch" href="/assets/js/47.7ed16fc7.js"><link rel="prefetch" href="/assets/js/48.c982d5ed.js"><link rel="prefetch" href="/assets/js/49.a7579f55.js"><link rel="prefetch" href="/assets/js/5.08802d7d.js"><link rel="prefetch" href="/assets/js/50.103b5bf6.js"><link rel="prefetch" href="/assets/js/51.0fe9d79a.js"><link rel="prefetch" href="/assets/js/52.9ba31e26.js"><link rel="prefetch" href="/assets/js/53.0e8bc1f0.js"><link rel="prefetch" href="/assets/js/54.9566e517.js"><link rel="prefetch" href="/assets/js/55.a124abae.js"><link rel="prefetch" href="/assets/js/56.d9cf0800.js"><link rel="prefetch" href="/assets/js/57.93599da0.js"><link rel="prefetch" href="/assets/js/58.d943f85b.js"><link rel="prefetch" href="/assets/js/59.50a66488.js"><link rel="prefetch" href="/assets/js/6.a3ea60eb.js"><link rel="prefetch" href="/assets/js/60.21aa3aa3.js"><link rel="prefetch" href="/assets/js/61.6712c00f.js"><link rel="prefetch" href="/assets/js/62.eff3e4b1.js"><link rel="prefetch" href="/assets/js/63.09701d5a.js"><link rel="prefetch" href="/assets/js/64.eb440dec.js"><link rel="prefetch" href="/assets/js/65.aeed0579.js"><link rel="prefetch" href="/assets/js/66.97244c64.js"><link rel="prefetch" href="/assets/js/67.e01c5c24.js"><link rel="prefetch" href="/assets/js/68.21be91ba.js"><link rel="prefetch" href="/assets/js/69.c0849905.js"><link rel="prefetch" href="/assets/js/7.7fd40e91.js"><link rel="prefetch" href="/assets/js/70.b32bbe5d.js"><link rel="prefetch" href="/assets/js/71.0efbc0c7.js"><link rel="prefetch" href="/assets/js/72.ef963181.js"><link rel="prefetch" href="/assets/js/73.ca7dd5db.js"><link rel="prefetch" href="/assets/js/74.4483ede8.js"><link rel="prefetch" href="/assets/js/75.374ab483.js"><link rel="prefetch" href="/assets/js/76.b4a39f08.js"><link rel="prefetch" href="/assets/js/77.6b30c3cd.js"><link rel="prefetch" href="/assets/js/78.15376c33.js"><link rel="prefetch" href="/assets/js/79.3153fcec.js"><link rel="prefetch" href="/assets/js/80.9a88c684.js"><link rel="prefetch" href="/assets/js/81.1e3f842c.js"><link rel="prefetch" href="/assets/js/82.996dbd3d.js"><link rel="prefetch" href="/assets/js/83.955158bf.js"><link rel="prefetch" href="/assets/js/84.71bdc76d.js"><link rel="prefetch" href="/assets/js/85.774e49f2.js"><link rel="prefetch" href="/assets/js/86.bebf32e5.js"><link rel="prefetch" href="/assets/js/87.becdbde1.js"><link rel="prefetch" href="/assets/js/88.49e933f4.js"><link rel="prefetch" href="/assets/js/89.eeceedfd.js"><link rel="prefetch" href="/assets/js/90.3ea6dd12.js"><link rel="prefetch" href="/assets/js/91.62a6a556.js"><link rel="prefetch" href="/assets/js/92.e2ebb8f5.js"><link rel="prefetch" href="/assets/js/93.dcdefe7a.js"><link rel="prefetch" href="/assets/js/94.bf412146.js"><link rel="prefetch" href="/assets/js/95.8deadcdc.js"><link rel="prefetch" href="/assets/js/96.9977087a.js"><link rel="prefetch" href="/assets/js/97.6591f9da.js"><link rel="prefetch" href="/assets/js/98.4db7f75e.js"><link rel="prefetch" href="/assets/js/99.a61462e9.js"><link rel="prefetch" href="/assets/js/vendors~docsearch.2852b102.js"> <link rel="stylesheet" href="/assets/css/0.styles.146197cf.css">
  </head>
  <body class="theme-mode-light">
    <div id="app" data-server-rendered="true"><div class="theme-container sidebar-open have-rightmenu"><header class="navbar blur"><div title="目录" class="sidebar-button"><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" role="img" viewBox="0 0 448 512" width="50" height="50" class="icon"><path fill="currentColor" d="M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z"></path></svg></div> <a href="/" class="home-link router-link-active"><img src="https://p3-passport.byteacctimg.com/img/user-avatar/794fdae4ff249d532da19a3c26d420ed~300x300.image" alt="aiyoudiao" class="logo"> <span class="site-name can-hide">
      aiyoudiao
    </span></a> <div class="links"><div class="sky-switch" data-v-3a03d589><label for="toggle" data-v-3a03d589><input id="toggle" type="checkbox" data-v-3a03d589><div data-v-3a03d589></div></label></div> <div class="search-box"><input aria-label="Search" autocomplete="off" spellcheck="false" value=""> <!----></div> <nav class="nav-links can-hide"><div class="nav-item"><div class="dropdown-wrapper"><button type="button" aria-label="笔记" class="dropdown-title"><!----> <span class="title" style="display:;">笔记</span> <span class="arrow right"></span></button> <ul class="nav-dropdown" style="display:none;"><li class="dropdown-item"><!----> <a href="/pages/84633490449/" aria-current="page" class="nav-link router-link-exact-active router-link-active">
  JavaScript
</a></li><li class="dropdown-item"><!----> <a href="/pages/2331001041/" class="nav-link">
  Vue
</a></li><li class="dropdown-item"><!----> <a href="/pages/18114480448/" class="nav-link">
  React
</a></li><li class="dropdown-item"><!----> <a href="/pages/25236260426/" class="nav-link">
  低代码
</a></li><li class="dropdown-item"><!----> <a href="/pages/35345230523/" class="nav-link">
  线性系统
</a></li><li class="dropdown-item"><!----> <a href="/pages/08313561056/" class="nav-link">
  暂未分类
</a></li></ul></div></div><div class="nav-item"><div class="dropdown-wrapper"><button type="button" aria-label="算法与设计" class="dropdown-title"><!----> <span class="title" style="display:;">算法与设计</span> <span class="arrow right"></span></button> <ul class="nav-dropdown" style="display:none;"><li class="dropdown-item"><!----> <a href="/pages/70741550255/" class="nav-link">
  LeetCode
</a></li><li class="dropdown-item"><!----> <a href="/pages/17845450445/" class="nav-link">
  算法
</a></li><li class="dropdown-item"><!----> <a href="/pages/90132170217/" class="nav-link">
  数据结构
</a></li><li class="dropdown-item"><!----> <a href="/pages/50546120212/" class="nav-link">
  设计模式
</a></li><li class="dropdown-item"><!----> <a href="/pages/02344550255/" class="nav-link">
  Other
</a></li></ul></div></div><div class="nav-item"><div class="dropdown-wrapper"><button type="button" aria-label="技能" class="dropdown-title"><!----> <span class="title" style="display:;">技能</span> <span class="arrow right"></span></button> <ul class="nav-dropdown" style="display:none;"><li class="dropdown-item"><!----> <a href="/pages/82158160216/" class="nav-link">
  PMP
</a></li><li class="dropdown-item"><!----> <a href="/pages/41858590259/" class="nav-link">
  Office
</a></li><li class="dropdown-item"><!----> <a href="/pages/02359360236/" class="nav-link">
  面试
</a></li><li class="dropdown-item"><!----> <a href="/pages/73600130213/" class="nav-link">
  Bash
</a></li></ul></div></div><div class="nav-item"><div class="dropdown-wrapper"><button type="button" aria-label="历程" class="dropdown-title"><!----> <span class="title" style="display:;">历程</span> <span class="arrow right"></span></button> <ul class="nav-dropdown" style="display:none;"><li class="dropdown-item"><!----> <a href="/pages/83857320232/" class="nav-link">
  流年往事
</a></li><li class="dropdown-item"><!----> <a href="/pages/93419130213/" class="nav-link">
  经验片段
</a></li><li class="dropdown-item"><!----> <a href="/pages/99744220322/" class="nav-link">
  读书杂感
</a></li></ul></div></div><div class="nav-item"><div class="dropdown-wrapper"><button type="button" aria-label="首页" class="dropdown-title"><!----> <span class="title" style="display:;">首页</span> <span class="arrow right"></span></button> <ul class="nav-dropdown" style="display:none;"><li class="dropdown-item"><!----> <a href="/archives/" class="nav-link">
  归档
</a></li><li class="dropdown-item"><!----> <a href="/categories/" class="nav-link">
  分类
</a></li><li class="dropdown-item"><!----> <a href="/tags/" class="nav-link">
  标签
</a></li></ul></div></div><div class="nav-item"><div class="dropdown-wrapper"><button type="button" aria-label="其它" class="dropdown-title"><!----> <span class="title" style="display:;">其它</span> <span class="arrow right"></span></button> <ul class="nav-dropdown" style="display:none;"><li class="dropdown-item"><!----> <a href="/pages/02657130213/" class="nav-link">
  简介
</a></li><li class="dropdown-item"><!----> <a href="/pages/5390102042/" class="nav-link">
  收藏
</a></li><li class="dropdown-item"><!----> <a href="/pages/32309510451/" class="nav-link">
  有趣
</a></li><li class="dropdown-item"><!----> <a href="/pages/23313210521/" class="nav-link">
  文档
</a></li></ul></div></div> <!----></nav></div></header> <div class="sidebar-mask"></div> <div class="sidebar-hover-trigger"></div> <aside class="sidebar" style="display:none;"><div class="blogger"><img src="/img/mar.jpg"> <div class="blogger-info"><h3>码二</h3> <span>扫微信二维码，认识一下码二吧😉。</span></div></div> <nav class="nav-links"><div class="nav-item"><div class="dropdown-wrapper"><button type="button" aria-label="笔记" class="dropdown-title"><!----> <span class="title" style="display:;">笔记</span> <span class="arrow right"></span></button> <ul class="nav-dropdown" style="display:none;"><li class="dropdown-item"><!----> <a href="/pages/84633490449/" aria-current="page" class="nav-link router-link-exact-active router-link-active">
  JavaScript
</a></li><li class="dropdown-item"><!----> <a href="/pages/2331001041/" class="nav-link">
  Vue
</a></li><li class="dropdown-item"><!----> <a href="/pages/18114480448/" class="nav-link">
  React
</a></li><li class="dropdown-item"><!----> <a href="/pages/25236260426/" class="nav-link">
  低代码
</a></li><li class="dropdown-item"><!----> <a href="/pages/35345230523/" class="nav-link">
  线性系统
</a></li><li class="dropdown-item"><!----> <a href="/pages/08313561056/" class="nav-link">
  暂未分类
</a></li></ul></div></div><div class="nav-item"><div class="dropdown-wrapper"><button type="button" aria-label="算法与设计" class="dropdown-title"><!----> <span class="title" style="display:;">算法与设计</span> <span class="arrow right"></span></button> <ul class="nav-dropdown" style="display:none;"><li class="dropdown-item"><!----> <a href="/pages/70741550255/" class="nav-link">
  LeetCode
</a></li><li class="dropdown-item"><!----> <a href="/pages/17845450445/" class="nav-link">
  算法
</a></li><li class="dropdown-item"><!----> <a href="/pages/90132170217/" class="nav-link">
  数据结构
</a></li><li class="dropdown-item"><!----> <a href="/pages/50546120212/" class="nav-link">
  设计模式
</a></li><li class="dropdown-item"><!----> <a href="/pages/02344550255/" class="nav-link">
  Other
</a></li></ul></div></div><div class="nav-item"><div class="dropdown-wrapper"><button type="button" aria-label="技能" class="dropdown-title"><!----> <span class="title" style="display:;">技能</span> <span class="arrow right"></span></button> <ul class="nav-dropdown" style="display:none;"><li class="dropdown-item"><!----> <a href="/pages/82158160216/" class="nav-link">
  PMP
</a></li><li class="dropdown-item"><!----> <a href="/pages/41858590259/" class="nav-link">
  Office
</a></li><li class="dropdown-item"><!----> <a href="/pages/02359360236/" class="nav-link">
  面试
</a></li><li class="dropdown-item"><!----> <a href="/pages/73600130213/" class="nav-link">
  Bash
</a></li></ul></div></div><div class="nav-item"><div class="dropdown-wrapper"><button type="button" aria-label="历程" class="dropdown-title"><!----> <span class="title" style="display:;">历程</span> <span class="arrow right"></span></button> <ul class="nav-dropdown" style="display:none;"><li class="dropdown-item"><!----> <a href="/pages/83857320232/" class="nav-link">
  流年往事
</a></li><li class="dropdown-item"><!----> <a href="/pages/93419130213/" class="nav-link">
  经验片段
</a></li><li class="dropdown-item"><!----> <a href="/pages/99744220322/" class="nav-link">
  读书杂感
</a></li></ul></div></div><div class="nav-item"><div class="dropdown-wrapper"><button type="button" aria-label="首页" class="dropdown-title"><!----> <span class="title" style="display:;">首页</span> <span class="arrow right"></span></button> <ul class="nav-dropdown" style="display:none;"><li class="dropdown-item"><!----> <a href="/archives/" class="nav-link">
  归档
</a></li><li class="dropdown-item"><!----> <a href="/categories/" class="nav-link">
  分类
</a></li><li class="dropdown-item"><!----> <a href="/tags/" class="nav-link">
  标签
</a></li></ul></div></div><div class="nav-item"><div class="dropdown-wrapper"><button type="button" aria-label="其它" class="dropdown-title"><!----> <span class="title" style="display:;">其它</span> <span class="arrow right"></span></button> <ul class="nav-dropdown" style="display:none;"><li class="dropdown-item"><!----> <a href="/pages/02657130213/" class="nav-link">
  简介
</a></li><li class="dropdown-item"><!----> <a href="/pages/5390102042/" class="nav-link">
  收藏
</a></li><li class="dropdown-item"><!----> <a href="/pages/32309510451/" class="nav-link">
  有趣
</a></li><li class="dropdown-item"><!----> <a href="/pages/23313210521/" class="nav-link">
  文档
</a></li></ul></div></div> <!----></nav>  <ul class="sidebar-links"><li><section class="sidebar-group collapsable depth-0"><p class="sidebar-heading open"><span>JavaScript</span> <span class="arrow down"></span></p> <ul class="sidebar-links sidebar-group-items"><li><a href="/pages/84633490449/" aria-current="page" class="active sidebar-link">实现一个vdom</a><ul class="sidebar-sub-headers"><li class="sidebar-sub-header"><a href="/pages/84633490449/#前言" class="sidebar-link">前言</a></li><li class="sidebar-sub-header"><a href="/pages/84633490449/#准备工作" class="sidebar-link">准备工作</a></li><li class="sidebar-sub-header"><a href="/pages/84633490449/#设计思路" class="sidebar-link">设计思路</a><ul class="sidebar-sub-headers"><li class="sidebar-sub-header"><a href="/pages/84633490449/#dom操作" class="sidebar-link">dom操作</a></li><li class="sidebar-sub-header"><a href="/pages/84633490449/#diff-算法" class="sidebar-link">diff 算法</a></li><li class="sidebar-sub-header"><a href="/pages/84633490449/#h函数" class="sidebar-link">h函数</a></li><li class="sidebar-sub-header"><a href="/pages/84633490449/#renderer-渲染器" class="sidebar-link">renderer 渲染器</a></li></ul></li><li class="sidebar-sub-header"><a href="/pages/84633490449/#代码实现" class="sidebar-link">代码实现</a></li><li class="sidebar-sub-header"><a href="/pages/84633490449/#总结" class="sidebar-link">总结</a></li></ul></li><li><a href="/pages/42534310431/" class="sidebar-link">前端路由监听</a></li><li><a href="/pages/8985100040/" class="sidebar-link">定制自己的代码片段</a></li><li><a href="/pages/70903450545/" class="sidebar-link">写一个下载文件功能</a></li><li><a href="/pages/00457481048/" class="sidebar-link">正则表达式</a></li><li><a href="/pages/26253370137/" class="sidebar-link">JS性能优化</a></li><li><a href="/pages/97717370337/" class="sidebar-link">JS性能优化进阶</a></li></ul></section></li><li><section class="sidebar-group collapsable depth-0"><p class="sidebar-heading"><span>vue</span> <span class="arrow right"></span></p> <!----></section></li><li><section class="sidebar-group collapsable depth-0"><p class="sidebar-heading"><span>react</span> <span class="arrow right"></span></p> <!----></section></li><li><section class="sidebar-group collapsable depth-0"><p class="sidebar-heading"><span>低代码</span> <span class="arrow right"></span></p> <!----></section></li><li><section class="sidebar-group collapsable depth-0"><p class="sidebar-heading"><span>读书会</span> <span class="arrow right"></span></p> <!----></section></li><li><section class="sidebar-group collapsable depth-0"><p class="sidebar-heading"><span>线性代数</span> <span class="arrow right"></span></p> <!----></section></li><li><section class="sidebar-group collapsable depth-0"><p class="sidebar-heading"><span>docker</span> <span class="arrow right"></span></p> <!----></section></li><li><section class="sidebar-group collapsable depth-0"><p class="sidebar-heading"><span>auto</span> <span class="arrow right"></span></p> <!----></section></li><li><section class="sidebar-group collapsable depth-0"><p class="sidebar-heading"><span>杂记</span> <span class="arrow right"></span></p> <!----></section></li></ul> </aside> <div><main class="page"> <div class="theme-vdoing-wrapper bg-style-1"><div class="articleInfo-wrap" data-v-18fb2c02><div class="articleInfo" data-v-18fb2c02><ul class="breadcrumbs" data-v-18fb2c02><li data-v-18fb2c02><a href="/" title="首页" class="iconfont icon-home router-link-active" data-v-18fb2c02></a></li> <li data-v-18fb2c02><a href="/categories/?category=%E7%AC%94%E8%AE%B0" title="分类" data-v-18fb2c02>
          笔记
        </a></li> <li data-v-18fb2c02><a href="/categories/?category=JavaScript" title="分类" data-v-18fb2c02>
          JavaScript
        </a></li> <!----></ul> <div class="info" data-v-18fb2c02><div title="作者" class="author iconfont icon-touxiang" data-v-18fb2c02><a href="javascript:;" data-v-18fb2c02>aiyoudiao</a></div> <div title="创建时间" class="date iconfont icon-riqi" data-v-18fb2c02><a href="javascript:;" data-v-18fb2c02>2022-04-09</a></div> <!----></div></div></div> <!----> <div class="content-wrapper"><div class="right-menu-wrapper"><div class="right-menu-margin"><div class="right-menu-content"></div></div></div> <h1><!----> <span>
            实现一个vdom
          </span></h1> <div class="theme-vdoing-content content__default"><h2 id="前言"><a href="#前言" class="header-anchor">#</a> 前言</h2> <p>在vue和react中都有用到使用vdom来进行渲染界面，在页面刚创建的时候使用vdom来渲染界面性能并没有直接使用dom操作来渲染页面好，但是在更新界面的时候使用虚拟dom比直接使用dom操作要性能好，当然你如果原生dom操作非常熟练、经验也非常丰富，可能也有例外，不过那么多原生dom操作也会有比较大的心智压力。</p> <h2 id="准备工作"><a href="#准备工作" class="header-anchor">#</a> 准备工作</h2> <p>vite 官网：<a href="https://cn.vitejs.dev/guide/" target="_blank" rel="noopener noreferrer">https://cn.vitejs.dev/guide/<span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a></p> <p>使用vite是因为它比较快，也很成熟，支持多种模板，实现虚拟dom的话只需要空的模板即可，然后自己加个jest，然后再加个esm 转 cjs的babel插件即可，因为jset默认不支持import这样的语法。</p> <p>jset 官网：<a href="https://jestjs.io/zh-Hans/docs/getting-started" target="_blank" rel="noopener noreferrer">https://jestjs.io/zh-Hans/docs/getting-started<span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a></p> <p>babel插件：<a href="https://babel.dev/docs/en/babel-plugin-transform-modules-commonjs" target="_blank" rel="noopener noreferrer">@babel/plugin-transform-modules-commonjs<span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a></p> <div class="language-bash extra-class"><pre class="language-bash"><code><span class="token function">yarn</span> create vite vdom
<span class="token builtin class-name">cd</span> vdom
<span class="token function">yarn</span> <span class="token function">install</span>
<span class="token function">yarn</span> <span class="token function">add</span> jset
<span class="token function">yarn</span> <span class="token function">add</span> @babel/plugin-transform-modules-commonjs
</code></pre></div><p>其它的看<a href="https://github.com/aiyoudiao/aiyou-vdom" target="_blank" rel="noopener noreferrer">仓库代码<span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a>，能正常运行，也都有注释，代码是基于下面的设计思路来实现的，没那么复杂。在浏览器直接看代码，点击 <a href="https://github1s.com/aiyoudiao/aiyou-vdom" target="_blank" rel="noopener noreferrer">github1s<span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a>。</p> <h2 id="设计思路"><a href="#设计思路" class="header-anchor">#</a> 设计思路</h2> <p>四个操作：正常的虚拟dom操作，diff算法，h函数返回虚拟dom，还有renderer渲染器将dom插入进视图。</p> <h3 id="dom操作"><a href="#dom操作" class="header-anchor">#</a> dom操作</h3> <p>用过jquery就知道，dom操作也无非就是增删改查。</p> <div class="language-ts extra-class"><pre class="language-ts"><code><span class="token comment">// 新增</span>
<span class="token function">createElement</span><span class="token punctuation">(</span>typeName<span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">)</span><span class="token operator">:</span> HTMLDomElement<span class="token punctuation">;</span>
<span class="token function">insert</span><span class="token punctuation">(</span>el<span class="token operator">:</span>HTMLDomElement<span class="token punctuation">,</span> parent<span class="token operator">:</span>HTMLDomElement<span class="token punctuation">)</span><span class="token operator">:</span> <span class="token keyword">void</span><span class="token punctuation">;</span>
<span class="token function">createText</span><span class="token punctuation">(</span>text<span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">)</span><span class="token operator">:</span> Text<span class="token punctuation">;</span>

<span class="token comment">// 删除</span>
<span class="token function">remove</span><span class="token punctuation">(</span>el<span class="token operator">:</span>HTMLDomElement<span class="token punctuation">,</span> parent<span class="token operator">:</span>HTMLDomElement<span class="token punctuation">)</span><span class="token operator">:</span> <span class="token keyword">void</span><span class="token punctuation">;</span>

<span class="token comment">// 修改</span>
<span class="token function">replaceElement</span><span class="token punctuation">(</span>oldEle<span class="token operator">:</span>HTMLDomElement<span class="token punctuation">,</span> newEle<span class="token operator">:</span>HTMLDomElement<span class="token punctuation">)</span><span class="token operator">:</span> <span class="token keyword">void</span><span class="token punctuation">;</span>
<span class="token function">setText</span><span class="token punctuation">(</span>el<span class="token operator">:</span>HTMLDomElement<span class="token punctuation">,</span> text<span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">)</span><span class="token operator">:</span> <span class="token keyword">void</span><span class="token punctuation">;</span>
<span class="token function">setInnerHTML</span><span class="token punctuation">(</span>el<span class="token operator">:</span>HTMLDomElement<span class="token punctuation">,</span> text<span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">)</span><span class="token operator">:</span> <span class="token keyword">void</span><span class="token punctuation">;</span>

<span class="token comment">// 事件的名称纠正和绑定，然后属性的替换、设置和移除</span>
<span class="token function">patchProp</span><span class="token punctuation">(</span>el<span class="token operator">:</span> HTMLDomElement<span class="token punctuation">,</span> key<span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">,</span> prevVal<span class="token operator">:</span> <span class="token builtin">any</span><span class="token punctuation">,</span> nextVal<span class="token operator">:</span> <span class="token builtin">any</span><span class="token punctuation">)</span><span class="token operator">:</span> <span class="token keyword">void</span><span class="token punctuation">;</span>
</code></pre></div><h3 id="diff-算法"><a href="#diff-算法" class="header-anchor">#</a> diff 算法</h3> <p>这里只做大概的diff设计，简单理解的diff，diff开发中比较常见的，比如git diff、code diff。</p> <p>先对比tag，不一样就直接替换。</p> <p>tag一样就检测props。props存在的话，props中新节点不是老节点，直接替换。props中老节点有但是新节点没有，那么全删掉。<br>
然后检测children，children有两种，一种是字符串，一种是数组。<br>
新的children是字符串，而之前的children也是字符串，那就对比字符串，看看是否要替换。<br>
新的children是字符串，之前的children是数组，那就整个替换。<br>
新的children是数组，而之前的children是字符串，那就把之前的节点清空，再遍历挂载新children中的的元素。
新的children是数组，而之前的children也是数组，这里就复杂一点了，需要递归。简单的话就是按照新旧节点中最少的节点数来进行循环递归，然后看看有没有必要移除掉旧节点中多余的节点，比如旧节点比新节点多。最后看看有没有必要添加新的节点，比如新的节点比旧节点多。</p> <p>递归是一个深度优先遍历的过程，并不复杂，我有一篇文章有写<a href="https://juejin.cn/post/7078986034976915463" target="_blank" rel="noopener noreferrer">通过链表来思考递归<span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a>，递归和回溯的设计是接触算法比较友好的思想的噢，不会很难。</p> <h3 id="h函数"><a href="#h函数" class="header-anchor">#</a> h函数</h3> <p>h用于返回一个虚拟dom，类似一个集装箱吧，把虚拟dom组装好，返回给你。这里只做简单的实现，理解思想就行。</p> <div class="language-ts extra-class"><pre class="language-ts"><code><span class="token comment">// 这里偷了点懒，不过理解它的入参类型即可，不用纠结具体的参数中每一个类型具体的细节</span>
<span class="token function">h</span><span class="token punctuation">(</span>tag<span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">,</span> props<span class="token operator">:</span> object<span class="token punctuation">,</span> children<span class="token operator">:</span> <span class="token builtin">any</span><span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token operator">:</span> object<span class="token punctuation">;</span>
</code></pre></div><h3 id="renderer-渲染器"><a href="#renderer-渲染器" class="header-anchor">#</a> renderer 渲染器</h3> <p>这里实现一个mountElement就行了，将vnode渲染成真实的dom，处理props，处理children，插入到视图中，返回传入的vonde。</p> <div class="language-ts extra-class"><pre class="language-ts"><code><span class="token function">mountElement</span><span class="token punctuation">(</span>vnode<span class="token operator">:</span> object<span class="token punctuation">,</span> container<span class="token operator">:</span> <span class="token builtin">string</span><span class="token operator">|</span>HTMLDomElement<span class="token punctuation">)</span><span class="token operator">:</span> object<span class="token punctuation">;</span>
</code></pre></div><h2 id="代码实现"><a href="#代码实现" class="header-anchor">#</a> 代码实现</h2> <p><strong>dom.js</strong></p> <div class="language-js extra-class"><pre class="language-js"><code>
<span class="token keyword">import</span> <span class="token punctuation">{</span> log <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">'./_log.js'</span>

<span class="token comment">/* 新增相关 */</span>

<span class="token keyword">export</span> <span class="token keyword">function</span> <span class="token function">createElement</span><span class="token punctuation">(</span><span class="token parameter">typeName</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token function">log</span><span class="token punctuation">(</span><span class="token string">'创建元素'</span><span class="token punctuation">,</span><span class="token punctuation">[</span><span class="token string">'createElement'</span><span class="token punctuation">]</span><span class="token punctuation">)</span>
    <span class="token keyword">return</span> document<span class="token punctuation">.</span><span class="token function">createElement</span><span class="token punctuation">(</span>typeName<span class="token punctuation">)</span>
<span class="token punctuation">}</span>

<span class="token keyword">export</span> <span class="token keyword">function</span> <span class="token function">insert</span> <span class="token punctuation">(</span><span class="token parameter">el<span class="token punctuation">,</span> parent</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token function">log</span><span class="token punctuation">(</span><span class="token string">'插入元素'</span><span class="token punctuation">,</span><span class="token punctuation">[</span><span class="token string">'insert'</span><span class="token punctuation">]</span><span class="token punctuation">)</span>
    parent<span class="token punctuation">.</span><span class="token function">append</span><span class="token punctuation">(</span>el<span class="token punctuation">)</span>
<span class="token punctuation">}</span>

<span class="token keyword">export</span> <span class="token keyword">function</span> <span class="token function">createText</span><span class="token punctuation">(</span><span class="token parameter">text</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token function">log</span><span class="token punctuation">(</span><span class="token string">'创建文本'</span><span class="token punctuation">,</span><span class="token punctuation">[</span><span class="token string">'createText'</span><span class="token punctuation">]</span><span class="token punctuation">)</span>
    <span class="token keyword">return</span> <span class="token keyword">new</span> <span class="token class-name">Text</span><span class="token punctuation">(</span>text<span class="token punctuation">)</span>
<span class="token punctuation">}</span>

<span class="token comment">/* 删除相关 */</span>

<span class="token keyword">export</span> <span class="token keyword">function</span> <span class="token function">remove</span> <span class="token punctuation">(</span><span class="token parameter">el<span class="token punctuation">,</span> parent</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token function">log</span><span class="token punctuation">(</span><span class="token string">'移除元素'</span><span class="token punctuation">,</span><span class="token punctuation">[</span><span class="token string">'insert'</span><span class="token punctuation">]</span><span class="token punctuation">)</span>
    parent<span class="token punctuation">.</span><span class="token function">remove</span><span class="token punctuation">(</span>el<span class="token punctuation">)</span>
<span class="token punctuation">}</span>

<span class="token comment">/* 修改相关 */</span>

<span class="token keyword">export</span> <span class="token keyword">function</span> <span class="token function">replaceElement</span> <span class="token punctuation">(</span><span class="token parameter">oldEle<span class="token punctuation">,</span> newEle</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token function">log</span><span class="token punctuation">(</span><span class="token string">'替换元素'</span><span class="token punctuation">,</span><span class="token punctuation">[</span><span class="token string">'repalceElement'</span><span class="token punctuation">]</span><span class="token punctuation">)</span>
    oldEle<span class="token punctuation">.</span><span class="token function">repaceWith</span><span class="token punctuation">(</span>newEle<span class="token punctuation">)</span>
<span class="token punctuation">}</span>

<span class="token keyword">export</span> <span class="token keyword">function</span> <span class="token function">setText</span> <span class="token punctuation">(</span><span class="token parameter">el<span class="token punctuation">,</span> text</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token function">log</span><span class="token punctuation">(</span><span class="token string">'设置文本'</span><span class="token punctuation">,</span> <span class="token punctuation">[</span><span class="token string">'setText'</span><span class="token punctuation">]</span><span class="token punctuation">)</span>
    el<span class="token punctuation">.</span>textContent <span class="token operator">=</span> text <span class="token comment">// 支持空格换行，innerText会把空白符都清除</span>
    <span class="token comment">// el.innerText = text</span>
<span class="token punctuation">}</span>

<span class="token keyword">export</span> <span class="token keyword">function</span> <span class="token function">setInnerHTML</span> <span class="token punctuation">(</span><span class="token parameter">el<span class="token punctuation">,</span> text</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token function">log</span><span class="token punctuation">(</span><span class="token string">'设置InnerHTML'</span><span class="token punctuation">,</span> <span class="token punctuation">[</span><span class="token string">'setInnerHTML'</span><span class="token punctuation">]</span><span class="token punctuation">)</span>
    el<span class="token punctuation">.</span>innerHTML <span class="token operator">=</span> text
<span class="token punctuation">}</span>

<span class="token comment">/* 事件的名称纠正和绑定，然后属性的替换、设置和移除 相关*/</span>
<span class="token keyword">export</span> <span class="token keyword">function</span> <span class="token function">patchProp</span> <span class="token punctuation">(</span><span class="token parameter">el<span class="token punctuation">,</span> key<span class="token punctuation">,</span> prevVal<span class="token punctuation">,</span> nextVal</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token function">log</span><span class="token punctuation">(</span><span class="token string">'patchProp'</span><span class="token punctuation">)</span>

    <span class="token keyword">if</span> <span class="token punctuation">(</span>key<span class="token punctuation">.</span><span class="token function">starsWith</span><span class="token punctuation">(</span><span class="token string">'on'</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
        <span class="token comment">// el.addEventListener(key.slice(2).toLowerCase(), nextVal)</span>
        el<span class="token punctuation">.</span><span class="token function">addEventListener</span><span class="token punctuation">(</span>key<span class="token punctuation">.</span><span class="token function">slice</span><span class="token punctuation">(</span><span class="token number">2</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">toLocaleLowerCase</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span> nextVal<span class="token punctuation">)</span> <span class="token comment">// 支持不同的语言环境时，采用本地地区的转换方法</span>
        <span class="token function">log</span><span class="token punctuation">(</span><span class="token string">'事件名称纠正和绑定'</span><span class="token punctuation">)</span>
        <span class="token keyword">return</span>
    <span class="token punctuation">}</span>

    <span class="token function">log</span><span class="token punctuation">(</span><span class="token string">'属性替换、设置或者移除'</span><span class="token punctuation">)</span>
    nextVal <span class="token operator">===</span> <span class="token keyword">null</span> <span class="token operator">?</span> el<span class="token punctuation">.</span><span class="token function">removeAttribute</span><span class="token punctuation">(</span>key<span class="token punctuation">)</span> <span class="token operator">:</span> el<span class="token punctuation">.</span><span class="token function">setAttribute</span><span class="token punctuation">(</span>key<span class="token punctuation">,</span> nextVal<span class="token punctuation">)</span>
<span class="token punctuation">}</span>

</code></pre></div><p><strong>h.js</strong></p> <div class="language-js extra-class"><pre class="language-js"><code><span class="token keyword">import</span> <span class="token punctuation">{</span> log <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">'./_log'</span>

<span class="token keyword">export</span> <span class="token keyword">function</span> <span class="token function">h</span><span class="token punctuation">(</span><span class="token parameter">tag<span class="token punctuation">,</span> props<span class="token punctuation">,</span> children <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token punctuation">]</span></span><span class="token punctuation">)</span><span class="token punctuation">{</span>
    <span class="token function">log</span><span class="token punctuation">(</span><span class="token string">'返回vdom'</span><span class="token punctuation">,</span><span class="token punctuation">[</span><span class="token string">'h'</span><span class="token punctuation">]</span><span class="token punctuation">)</span>
    <span class="token keyword">return</span> <span class="token punctuation">{</span>
        tag<span class="token punctuation">,</span> props<span class="token punctuation">,</span> children
    <span class="token punctuation">}</span>
<span class="token punctuation">}</span>

</code></pre></div><p><strong>renderer.js</strong></p> <div class="language-js extra-class"><pre class="language-js"><code><span class="token keyword">import</span> <span class="token punctuation">{</span>
    createElement<span class="token punctuation">,</span>
    patchProp<span class="token punctuation">,</span>
    insert<span class="token punctuation">,</span>
    createText
<span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">'./dom'</span>

<span class="token keyword">export</span> <span class="token keyword">function</span> <span class="token function">createMount</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>

    <span class="token keyword">return</span> mountElement
    <span class="token keyword">function</span> <span class="token function">mountElement</span><span class="token punctuation">(</span><span class="token parameter">vnode<span class="token punctuation">,</span> container</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
        vnode<span class="token punctuation">.</span>el <span class="token operator">=</span> <span class="token function">createElement</span><span class="token punctuation">(</span>vnode<span class="token punctuation">.</span>tag<span class="token punctuation">)</span>
        <span class="token keyword">const</span> el <span class="token operator">=</span> vnode<span class="token punctuation">.</span>el
        
        <span class="token comment">// 处理props</span>
        vnode<span class="token punctuation">.</span>props <span class="token operator">&amp;&amp;</span> Object<span class="token punctuation">.</span><span class="token function">keys</span><span class="token punctuation">(</span>vnode<span class="token punctuation">.</span>props<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">forEach</span><span class="token punctuation">(</span><span class="token parameter">keyName</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
            <span class="token keyword">const</span> val <span class="token operator">=</span> vnode<span class="token punctuation">.</span>props<span class="token punctuation">[</span>keyName<span class="token punctuation">]</span>
            <span class="token function">patchProp</span><span class="token punctuation">(</span>vnode<span class="token punctuation">.</span>el<span class="token punctuation">,</span> keyName<span class="token punctuation">,</span> <span class="token keyword">null</span><span class="token punctuation">,</span> val<span class="token punctuation">)</span>
        <span class="token punctuation">}</span><span class="token punctuation">)</span>

        <span class="token comment">// 处理children</span>
        Array<span class="token punctuation">.</span><span class="token function">isArray</span><span class="token punctuation">(</span>vnode<span class="token punctuation">.</span>children<span class="token punctuation">)</span> <span class="token operator">?</span> vnode<span class="token punctuation">.</span>children<span class="token punctuation">.</span><span class="token function">forEach</span><span class="token punctuation">(</span><span class="token parameter">v</span> <span class="token operator">=&gt;</span> <span class="token function">mountElement</span><span class="token punctuation">(</span>v<span class="token punctuation">,</span> el<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token operator">:</span> <span class="token function">insert</span><span class="token punctuation">(</span><span class="token function">createText</span><span class="token punctuation">(</span>vnode<span class="token punctuation">.</span>children<span class="token punctuation">)</span><span class="token punctuation">,</span> el<span class="token punctuation">)</span>

        <span class="token comment">// 插入到视图中</span>
        <span class="token function">insert</span><span class="token punctuation">(</span>el<span class="token punctuation">,</span> container<span class="token punctuation">)</span>

        <span class="token comment">// 返回vnode</span>
        <span class="token keyword">return</span> vnode
    <span class="token punctuation">}</span>

<span class="token punctuation">}</span>

</code></pre></div><p><strong>diff.js</strong></p> <div class="language-js extra-class"><pre class="language-js"><code><span class="token keyword">import</span> <span class="token punctuation">{</span>
    patchProp<span class="token punctuation">,</span>
    setText<span class="token punctuation">,</span>
    createElement<span class="token punctuation">,</span>
    replaceElement<span class="token punctuation">,</span>
    remove
<span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">'./dom'</span>

<span class="token keyword">export</span> <span class="token keyword">function</span> <span class="token function">createDiff</span><span class="token punctuation">(</span><span class="token parameter">mountElement</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    
    <span class="token keyword">return</span> diff
    <span class="token keyword">function</span> <span class="token function">diff</span> <span class="token punctuation">(</span><span class="token parameter">oldNode<span class="token punctuation">,</span> newNode</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
        <span class="token keyword">const</span> <span class="token punctuation">{</span> <span class="token literal-property property">props</span><span class="token operator">:</span> oldProps<span class="token punctuation">,</span> <span class="token literal-property property">el</span><span class="token operator">:</span> oldEl<span class="token punctuation">,</span> <span class="token literal-property property">tag</span><span class="token operator">:</span> oldTag<span class="token punctuation">,</span> <span class="token literal-property property">children</span><span class="token operator">:</span> oldChildren <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token punctuation">]</span> <span class="token punctuation">}</span> <span class="token operator">=</span> oldNode
        <span class="token keyword">const</span> <span class="token punctuation">{</span> <span class="token literal-property property">props</span><span class="token operator">:</span> newProps<span class="token punctuation">,</span> <span class="token literal-property property">tag</span><span class="token operator">:</span> newTag<span class="token punctuation">,</span> <span class="token literal-property property">children</span><span class="token operator">:</span> newChildren <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token punctuation">]</span> <span class="token punctuation">}</span> <span class="token operator">=</span> newNode

        <span class="token comment">// 标签不同 便可替换</span>
        <span class="token keyword">if</span> <span class="token punctuation">(</span>oldTag <span class="token operator">!==</span> newTag<span class="token punctuation">)</span> <span class="token punctuation">{</span>
            <span class="token function">replaceElement</span><span class="token punctuation">(</span>oldEl<span class="token punctuation">,</span> <span class="token function">createElement</span><span class="token punctuation">(</span>newTag<span class="token punctuation">)</span><span class="token punctuation">)</span>
            <span class="token keyword">return</span>
        <span class="token punctuation">}</span>

        <span class="token keyword">const</span> el <span class="token operator">=</span> newNode<span class="token punctuation">.</span>el <span class="token operator">=</span> oldEl

        <span class="token keyword">if</span> <span class="token punctuation">(</span>newProps<span class="token punctuation">)</span> <span class="token punctuation">{</span>
            <span class="token comment">// 新旧不同 便可替换</span>
            Object<span class="token punctuation">.</span><span class="token function">keys</span><span class="token punctuation">(</span>newProps<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">forEach</span><span class="token punctuation">(</span><span class="token parameter">keyName</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
                newProps<span class="token punctuation">[</span>keyName<span class="token punctuation">]</span> <span class="token operator">!==</span> oldProps<span class="token punctuation">[</span>keyName<span class="token punctuation">]</span> <span class="token operator">&amp;&amp;</span> <span class="token function">patchProp</span><span class="token punctuation">(</span>el<span class="token punctuation">,</span> keyName<span class="token punctuation">,</span> oldProps<span class="token punctuation">[</span>keyName<span class="token punctuation">]</span><span class="token punctuation">,</span> newProps<span class="token punctuation">[</span>keyName<span class="token punctuation">]</span><span class="token punctuation">)</span>
            <span class="token punctuation">}</span><span class="token punctuation">)</span>

            <span class="token comment">// 旧有新无 便可移除</span>
            Object<span class="token punctuation">.</span><span class="token function">keys</span><span class="token punctuation">(</span>oldProps<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">forEach</span><span class="token punctuation">(</span><span class="token parameter">keyName</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
                <span class="token punctuation">(</span><span class="token operator">!</span>newProps<span class="token punctuation">[</span>keyName<span class="token punctuation">]</span><span class="token punctuation">)</span> <span class="token operator">&amp;&amp;</span> <span class="token function">patchProp</span><span class="token punctuation">(</span>el<span class="token punctuation">,</span> keyName<span class="token punctuation">,</span> oldProps<span class="token punctuation">[</span>keyName<span class="token punctuation">]</span><span class="token punctuation">,</span> <span class="token keyword">null</span><span class="token punctuation">)</span>
            <span class="token punctuation">}</span><span class="token punctuation">)</span>
        <span class="token punctuation">}</span>
        <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token keyword">typeof</span> newChildren <span class="token operator">===</span> <span class="token string">'string'</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
            <span class="token comment">// 都是字符串，但值不同，那么直接替换</span>
            <span class="token keyword">typeof</span> oldChildren <span class="token operator">===</span> <span class="token string">'string'</span> <span class="token operator">&amp;&amp;</span> newChildren <span class="token operator">!==</span> oldChildren <span class="token operator">&amp;&amp;</span> <span class="token function">setText</span><span class="token punctuation">(</span>el<span class="token punctuation">,</span> newChildren<span class="token punctuation">)</span>

            <span class="token comment">// 新children为字符串，旧children为数组，那么直接整个替换</span>
            Array<span class="token punctuation">.</span><span class="token function">isArray</span><span class="token punctuation">(</span>oldChildren<span class="token punctuation">)</span> <span class="token operator">&amp;&amp;</span> <span class="token function">setText</span><span class="token punctuation">(</span>oldEl<span class="token punctuation">,</span> newChildren<span class="token punctuation">)</span>

            <span class="token keyword">return</span>
        <span class="token punctuation">}</span>

        <span class="token comment">// 非字符串 非数组，那就是错误的数据类型</span>
        <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span>Array<span class="token punctuation">.</span><span class="token function">isArray</span><span class="token punctuation">(</span>newChildren<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
            <span class="token keyword">throw</span> <span class="token string">'children is not string or array.'</span>
        <span class="token punctuation">}</span>

        <span class="token comment">// 旧children为字符串，新children为数组，那就先清空dom内容，再生成新children的dom内容挂载到之前清空的这个dom中</span>
        <span class="token keyword">typeof</span> oldChildren <span class="token operator">===</span> <span class="token string">'string'</span> <span class="token operator">&amp;&amp;</span> <span class="token punctuation">(</span><span class="token function">setText</span><span class="token punctuation">(</span>el<span class="token punctuation">,</span> <span class="token string">''</span><span class="token punctuation">)</span><span class="token punctuation">,</span> newChildren<span class="token punctuation">.</span><span class="token function">forEach</span><span class="token punctuation">(</span><span class="token parameter">vnode</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span> <span class="token function">mountElement</span><span class="token punctuation">(</span>vnode<span class="token punctuation">,</span> el<span class="token punctuation">)</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">)</span>

        <span class="token keyword">if</span> <span class="token punctuation">(</span>Array<span class="token punctuation">.</span><span class="token function">isArray</span><span class="token punctuation">(</span>oldChildren<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
            <span class="token keyword">const</span> <span class="token punctuation">[</span>oldLen<span class="token punctuation">,</span> newLen<span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token punctuation">[</span>newChildren<span class="token punctuation">.</span>length<span class="token punctuation">,</span> oldChildren<span class="token punctuation">.</span>length<span class="token punctuation">]</span>
            <span class="token keyword">const</span> minLen <span class="token operator">=</span> Math<span class="token punctuation">.</span><span class="token function">min</span><span class="token punctuation">(</span>oldLen<span class="token punctuation">,</span> newLen<span class="token punctuation">)</span>

            <span class="token comment">// 最小长度的对比，从左到右，按照顺序对比vnode</span>
            <span class="token keyword">for</span> <span class="token punctuation">(</span><span class="token keyword">let</span> i <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span> i <span class="token operator">&lt;</span> minLen<span class="token punctuation">;</span> i<span class="token operator">++</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
                <span class="token keyword">const</span> <span class="token punctuation">[</span>oldVnode<span class="token punctuation">,</span> newVnode<span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token punctuation">[</span>oldChildren<span class="token punctuation">[</span>i<span class="token punctuation">]</span><span class="token punctuation">,</span> newChildren<span class="token punctuation">[</span>i<span class="token punctuation">]</span><span class="token punctuation">]</span>
                <span class="token function">diff</span><span class="token punctuation">(</span>oldVnode<span class="token punctuation">,</span> newVnode<span class="token punctuation">)</span>
            <span class="token punctuation">}</span>

            <span class="token comment">// 移除多的节点</span>
            oldLen <span class="token operator">&gt;</span> minLen <span class="token operator">&amp;&amp;</span> oldChildren<span class="token punctuation">.</span><span class="token function">filter</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">_<span class="token punctuation">,</span> i</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> i <span class="token operator">&gt;=</span> minLen<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">forEach</span><span class="token punctuation">(</span><span class="token parameter">vnode</span> <span class="token operator">=&gt;</span> <span class="token function">remove</span><span class="token punctuation">(</span>vnode<span class="token punctuation">.</span>el<span class="token punctuation">,</span> el<span class="token punctuation">)</span><span class="token punctuation">)</span>

            <span class="token comment">// 添加少的节点</span>
            newLen <span class="token operator">&gt;</span> minLen <span class="token operator">&amp;&amp;</span> oldChildren<span class="token punctuation">.</span><span class="token function">filter</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">_<span class="token punctuation">,</span> i</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> i <span class="token operator">&gt;=</span> minLen<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">forEach</span><span class="token punctuation">(</span><span class="token parameter">vnode</span> <span class="token operator">=&gt;</span> <span class="token function">mountElement</span><span class="token punctuation">(</span>vnode<span class="token punctuation">,</span> el<span class="token punctuation">)</span><span class="token punctuation">)</span>

        <span class="token punctuation">}</span>


    <span class="token punctuation">}</span>
<span class="token punctuation">}</span>

</code></pre></div><h2 id="总结"><a href="#总结" class="header-anchor">#</a> 总结</h2> <p><img src="https://gitee.com/aiyoudiao/images/raw/master/2022/04/09/1649488620440-dcb89390-dedf-4616-99f5-348ae2a451d7.png" alt=""></p> <p><img src="https://gitee.com/aiyoudiao/images/raw/master/2022/04/09/1649488592532-2651f4a4-6a4d-458e-b735-8fb9eb7c7785.png" alt=""></p> <p>从图中看更新虚拟dom的节点在更新的时候是针对性的更新数据变更的那部分dom，而真实dom操作一般都是数据发生变化导致整个部分的dom都会重新渲染，从而让页面重新排版重新布局，性能损耗大同时体验也不好。</p> <p>虚拟DOM的存在是为了提高渲染的性能，减少多余的重绘和回流，原生DOM操作的话，当你数据发生变化，会整块替换。而虚拟dom通过diff对比找到需要更新的dom，从而针对性的替换那一小部分，所以性能更优。</p> <p>使用虚拟DOM算法的性能损耗 =  虚拟DOM增删改 + diff 真实DOM差异 + 针对性的一小部分渲染时的回流和重绘。<br>
操作真实DOM的的性能损耗 = 真实DOM的增删改 + 大部分或者小部分渲染时的回流和重绘。</p> <p>综上分析，如果你使用真实DOM的操作，在遇到很大的业务场景时，如果你的经验丰富并且写的非常好，那么肯定比虚拟DOM的性能好，但是这和个人水平及心智上的抗压压力有关系，没有谁愿意写那种非常冗余并且重复、还难以维护的代码、开发效率还低的代码。<br>
所以这就是虚拟dom存在的意义，让你的开发更加的便捷，同时降低了心智上的压力，能够更好的关注具体的业务从而做出更好的应用，性能在绝大多数情况下都是很不错的。</p></div></div> <div class="page-edit"><!----> <div class="tags"><a href="/tags/?tag=JavaScript" title="标签">#JavaScript</a><a href="/tags/?tag=vue" title="标签">#vue</a><a href="/tags/?tag=react" title="标签">#react</a></div> <div class="last-updated"><span class="prefix">上次更新时间:</span> <span class="time">10年18月2023日 01时57分53秒</span></div></div> <div class="page-nav-wapper"><div class="page-nav-centre-wrap"><!----> <a href="/pages/42534310431/" class="page-nav-centre page-nav-centre-next"><div class="tooltip">前端路由监听</div></a></div> <div class="page-nav"><p class="inner"><!----> <span class="next"><a href="/pages/42534310431/"> 前端路由监听 </a>
        →
      </span></p></div></div></div> <div class="article-list"><div class="article-title"><a href="/archives/" class="iconfont icon-bi">最近更新</a></div> <div class="article-wrapper"><dl><dd>01</dd> <dt><a href="/pages/45343271027/"><div>01.数据结构导论一览.md</div></a> <span>10-16</span></dt></dl><dl><dd>02</dd> <dt><a href="/pages/38850370637/"><div>30.2023年06月04日.md</div></a> <span>06-04</span></dt></dl><dl><dd>03</dd> <dt><a href="/pages/74707370537/"><div>08.与测量相关.md</div></a> <span>05-06</span></dt></dl> <dl><dd></dd> <dt><a href="/archives/" class="more">更多文章&gt;</a></dt></dl></div></div> </main></div> <div class="footer"><!----> 
  Theme by
  <a href="https://github.com/xugaoyi/vuepress-theme-vdoing" target="_blank" title="本站主题">Vdoing</a> 
    | Copyright © 2017-2023
    <span class="link">aiyoudiao 码二</span> <span>备案号：</span> <a href="https://beian.miit.gov.cn/#/Integrated/index" target="_blank" title="备案号">鄂ICP备2022002654号-1</a></div> <div class="buttons"><div title="返回顶部" class="button blur go-to-top iconfont icon-fanhuidingbu" style="display:none;"></div> <div title="去评论" class="button blur go-to-comment iconfont icon-pinglun" style="display:none;"></div></div> <!----> <!----> <!----></div><div class="global-ui"><div></div><APlayer audio="" fixed="true" theme="#b7daff" loop="loop" order="list" preload="auto" volume="0.7" mutex="true" lrc-type="3" list-max-height="250" storage-name="vuepress-plugin-meting" id="aplayer-fixed"></APlayer><div id="VuepressPluginLive2d"></div></div></div>
    <script src="/assets/js/app.bd2fbc77.js" defer></script><script src="/assets/js/3.72c9c947.js" defer></script><script src="/assets/js/102.c3e2dcae.js" defer></script><script src="/assets/js/42.4251ca36.js" defer></script>
  </body>
</html>
